package top.lishuoboy.springmvc.a4;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.StrUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.UriComponentsBuilder;
import top.lishuoboy.springmvc.a4.bean.User;

import javax.servlet.ServletContext;
import javax.servlet.http.*;
import java.io.IOException;
import java.security.Principal;
import java.time.ZoneId;
import java.util.*;

import static top.lishuoboy.dependency.base.jackson.JacksonUtil.bean2Str;

/**
 * 四、SpringMVC获取请求参数
 */
@Controller
@RequestMapping("/a4")
@Slf4j
public class ParamController {

    /**
     * 测试html
     * http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/request
     */
    @RequestMapping("/request")
    public String request() {
        return "a4/request";
    }

    /**
     * 1、通过ServletAPI获取将HttpServletRequest作为控制器方法的形参，此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象
     */
    // http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/1?name=张三&pwd=111
    @SneakyThrows
    @RequestMapping("/1")
    public String test1(HttpServletRequest request) {
        String name = request.getParameter("name");
        String pwd = request.getParameter("pwd");
        log.warn("name=={},pwd=={}", name, pwd);
        return "public";
    }

    /**
     * 2、通过控制器方法的形参获取请求参数在控制器方法的形参位置，设置和请求参数同名的形参，当浏览器发送请求，匹配到请求映射时，在DispatcherServlet中就会将请求参数赋值给相应的形参
     * 若请求所传输的请求参数中有多个同名的请求参数，此时可以在控制器方法的形参中设置字符串数组或者字符串类型的形参接收此请求参数
     * 若使用字符串数组类型的形参，此参数的数组中包含了每一个数据
     * 若使用字符串类型的形参，此参数的值为每个数据中间使用逗号拼接的结果
     * 原生 servletApi 同名参数用字符串只能拿到第一个参数的值，字符串数组能拿到所有value
     */
    // http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/2?name=张三&pwd=111
    // http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/2?name=张三&pwd=111&hobby=eat&hobby=drink&hobby2=eat2&hobby2=drink2
    @RequestMapping("/2")
    public String test2(String name, String pwd, String hobby, String[] hobby2, HttpServletRequest request) {
        log.warn("name=={},pwd=={},hobby=={},hobby2=={}", name, pwd, hobby, hobby2);
        // 原生 servletApi
        log.warn("原生 servletApi ： hobby=={},hobby2=={}", request.getParameter("hobby"), request.getParameterValues("hobby2"));
        return "public";
    }

    /**
     * 3、@RequestParam
     * "@RequestParam"是将请求参数和控制器方法的形参创建映射关系
     * "@RequestParam"注解一共有三个属性：
     * value：指定为形参赋值的请求参数的参数名
     * required：设置是否必须传输此请求参数，默认值为true。若设置为true时，则当前请求必须传输value所指定的请求参数，
     * 若没有传输该请求参数，且没有设置defaultValue属性，则页面报错400：Required String parameter 'xxx' is not present；若设置为false，则当前请求不是必须传输value所指定的请求参数，若没有传输，则注解所标识的形参的值为null
     * defaultValue：不管required属性值为true或false，当value所指定的请求参数没有传输或传输的值
     * 为""时，则使用默认值为形参赋值
     */
    // http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/3?name=张三&pwd=111
    // http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/3?pwd=111
    @RequestMapping("/3")
    public String test3(@RequestParam("name") String name, @RequestParam(value = "pwd222", required = false, defaultValue = "此为pwd默认值") String pwd) {
        log.warn("name=={}, pwd=={}", name, pwd);
        return "public";
    }

    /**
     * 4、@RequestHeader （类似 @RequestParam）
     * "@RequestHeader"是将请求头信息和控制器方法的形参创建映射关系
     * "@RequestHeader"注解一共有三个属性：value、required、defaultValue，用法同@RequestParam
     */
    // http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/4
    @RequestMapping("/4")
    public String test4(@RequestHeader String host, @RequestHeader(value = "connection", required = false, defaultValue = "这是默认值") String connection) {
        log.warn("host=={}, connection=={}", host, connection);
        return "public";
    }

    /**
     * 5、@CookieValue （类似 @RequestParam）
     * ”@CookieValue”是将cookie数据和控制器方法的形参创建映射关系
     * ”@CookieValue”注解一共有三个属性：value、required、defaultValue，用法同@RequestParam
     */
    // http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/5
    @RequestMapping("/5")
    public String test5(@CookieValue("JSESSIONID") String jsessionId, @CookieValue(value = "Idea-7cc5559e", required = false, defaultValue = "这是默认值") String Idea_7cc5559e) {
        log.warn("JSESSIONID=={}, Idea-7cc5559e=={}", jsessionId, Idea_7cc5559e);
        return "public";
    }

    /**
     * 6、通过POJO获取请求参数
     * 可以在控制器方法的形参位置设置一个实体类类型的形参，此时若浏览器传输的请求参数的参数名和实体类中的属性名一致，那么请求参数就会为此属性赋值
     */
    // http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/6?name=张三&pwd=111&hobby=eat&hobby=drink&hobby2=eat2&hobby2=drink2
    @RequestMapping("/6")
    public String test6(User user) {
        log.warn("user=={}", user);
        return "public";
    }

    /**
     * 8、通过Map获取请求参数（尚硅谷教程里没有）
     * 必须加 @RequestParam 注解
     * 缺点：
     * 1、重名参数hobby只能接收第一个value。日志：paramMap=={name=张三, pwd=111, hobby=eat, hobby2=eat2},丢参数了
     * 2、只用Map<String, String>即可。因为本质就是Map<String, String>
     * Map<String, Object>能接受也能用
     * Map<String, Map>、Map<String, List>能接收参数（本质还是<String, String>），但是在用到的时候会将String强转Map报错
     * 不能Propertie和List接收参数，直接报错
     */
    // http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/8?name=张三&pwd=111&hobby=eat&hobby=drink&hobby2=eat2&hobby2=drink2
    @RequestMapping("/8")
    public String test8(@RequestParam Map<String, String> paramMap, @RequestParam Map<String, Object> paramMap2, @RequestParam Map<String, Map> paramMap3, @RequestParam Map<String, List> paramMap4) {
        log.warn("paramMap=={}", paramMap);
        log.warn("paramMap2=={}", paramMap2);
        log.warn("paramMap3=={}", paramMap3);
        log.warn("paramMap4=={}", paramMap4);
        log.warn("paramMap.getClass()=={},  paramMap.get(name).getClass()=={}", paramMap.getClass(), paramMap.get("name").getClass());
        log.warn("paramMap2.getClass()=={}, paramMap.get(name).getClass()=={}", paramMap2.getClass(), paramMap2.get("name").getClass());
        // 以下两行报错
//        log.warn("paramMap3.getClass()=={}, paramMap.get(name).getClass()=={}", paramMap3.getClass(), paramMap3.get("name").getClass());
//        log.warn("paramMap4.getClass()=={}, paramMap.get(name).getClass()=={}", paramMap4.getClass(), paramMap4.get("name").getClass());
        return "public";
    }

    /**
     * 9、同时用servletAPI、形参、POJO、Map 获取请求参数（尚硅谷教程里没有）
     */
    // http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/9?name=张三&pwd=111&hobby=eat&hobby=drink&hobby2=eat2&hobby2=drink2
    @RequestMapping("/9")
    public String test9(String name, String pwd, String hobby, String[] hobby2, User user, User user2, @RequestParam Map<String, String> paramMap, @RequestParam Map<String, String> paramMap2, HttpServletRequest request) {
        log.warn("name=={}, pwd=={}, hobby=={}, hobby2=={}", name, pwd, hobby, hobby2);
        log.warn("user=={}", user);
        log.warn("user2=={}", user2);
        log.warn("paramMap=={},", paramMap);
        log.warn("paramMap2=={},", paramMap2);

        String name_ = request.getParameter("name");
        String pwd_ = request.getParameter("pwd");
        String[] hobby_ = request.getParameterValues("hobby");
        String[] hobby2_ = request.getParameterValues("hobby2");
        log.warn("name_=={}, pwd_=={}, hobby_=={}, hobby2_=={}", name_, pwd_, hobby_, hobby2_);

        return "public";
    }


    /**
     * 10、同时获取SpringMVC封装的rest参数、查询参数、请求头（包括cookie）、请求体、请求方法、请求属性、ModelMap，原生request、response、session（尚硅谷教程里没有）
     * 测试页面 http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/request.html
     * url http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/all/硕/吃,喝?name=张三&hobby=吃&hobby=喝&hobby2=嫖&hobby2=赌&hobby3=玩&hobby3=乐&photo=文档视频.md
     */
    @RequestMapping(value = "/all/{pName}/{pHobby}")
    @ResponseBody
    public String testAll(
        // rest路径参数
        @PathVariable("pName") String pName, @PathVariable String pHobby, @PathVariable("pHobby") String[] pHobby2, @PathVariable("pHobby") List<String> pHobby3,
        @PathVariable Map<String, String> pParamMap,
        // 请求参数
        @RequestParam("name") String name, String hobby, String[] hobby2, @RequestParam List hobby3,  // @RequestParam("name") 可以省略
        @RequestParam Map<String, String> paramMap,  // 【复选框丢数据】
        @ModelAttribute User user, //  【@ModelAttribute 可以省略】
        @RequestParam(value = "photo", required = false) MultipartFile multipartFile,  // 【post传文件才有】
        // 请求头
        @RequestHeader String host,
        @RequestHeader Map<String, String> headerMap,
        @RequestHeader HttpHeaders httpHeaders,
        // cookie
        @CookieValue(value = "JSESSIONID", required = false) String jsessionId,
        @CookieValue(value = "JSESSIONID", required = false) Cookie cookie,
        // request 属性
        @RequestAttribute(value = "requestAttr", required = false) Object requestAttr,
        // 请求体
        @RequestBody(required = false) String requestBody,      // 【post非传文件才有】
        // 请求方法
        HttpMethod httpMethod,  // HTTP 请求方法的 Java 5 枚举。 旨在与org.springframework.http.client.ClientHttpRequest和org.springframework.web.client.RestTemplate一起使用。
        // model
        Map map,
        Model model,        // 本质还是Map
        ModelMap modelMap,  // 本质还是Map
        ModelAndView modelAndView, // 包含Model、View、HttpStatus
        // 请求
        RequestEntity<String> requestEntity,
        WebRequest webRequest,                  // NativeWebRequest nativeWebRequest,
        @RequestPart(required = false) MultipartRequest multipartRequest, // MultipartHttpServletRequest multipartHttpServletRequest,
        // session 属性
        @SessionAttribute(value = "sessionAttr", required = false) Object sessionAttr,
        // session 状态
        SessionStatus sessionStatus,

        // 二、原生的非springMVC对象
        HttpServletRequest request,
        HttpServletResponse response,
        HttpSession session,
        // 请求体
//        InputStream inputStream,          // request、InputStream、Reader 只能用一个。意义不大，直接用request/response的get即可
//        Reader reader,                    // request、InputStream、Reader 只能用一个。意义不大，直接用request/response的get即可
        // 响应体
//        OutputStream outputStream,        // response、outputStream、writer 只能用一个。意义不大，直接用request/response的get即可
//        Writer writer,                    // response、outputStream、writer 只能用一个。意义不大，直接用request/response的get即可
        // Locale、Principal。意义不大，直接用request/response的get即可
        Locale locale,
        Principal principal,
        TimeZone timeZone,
        ZoneId zoneId,
        PushBuilder pushBuilder,
        UriComponentsBuilder uriComponentsBuilder,
        BindingResult bindingResult,
        Errors errors
    ) throws IOException {
        log.warn("==SpringMVC封装的rest参数、查询参数、请求头（包括cookie）、请求体、请求方法、请求属性、ModelMap====================================");
        log.info("pName=={}, pHobby=={}, pHobby2=={}, pHobby3=={}", pName, pHobby, pHobby2, pHobby3);   // pName==硕, pHobby==吃,喝, pHobby2==[吃, 喝], pHobby3==[吃, 喝]
        log.info("pParamMap=={}", pParamMap);           // pParamMap=={pName=硕, pHobby=吃,喝}
        log.info("name=={}, hobby=={}, hobby2=={}, hobby3=={}", name, hobby, hobby2, hobby3);           // name==张三, hobby==吃,喝, hobby2==[嫖, 赌], hobby3==[玩, 乐]
        log.info("paramMap=={}", paramMap);             // 【复选框丢数据】 paramMap=={name=张三, hobby=吃, hobby2=嫖, hobby3=玩, photo=文档视频.md}
        log.info("user=={}", user);                     // user==User(id=null, name=张三, hobby=吃,喝, hobby2=[嫖, 赌], hobby3=[玩, 乐], multipartFile=null)
        log.info("multipartFile=={}", multipartFile);   // 【post传文件才有】 multipartFile==org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@2e343678
        log.info("host=={}", host);                     // host==localhost:8080
        // get时           headerMap=={host=localhost:8080, connection=keep-alive,                                              sec-ch-ua=" Not;A Brand";v="99", "Microsoft Edge";v="97", "Chromium";v="97", sec-ch-ua-mobile=?0, sec-ch-ua-platform="Windows", upgrade-insecure-requests=1,                                                                               user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.69, accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9, sec-fetch-site=same-origin, sec-fetch-mode=navigate, sec-fetch-user=?1, sec-fetch-dest=document, referer=http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/request.html, accept-encoding=gzip, deflate, br, accept-language=zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7, cookie=JSESSIONID=7234876432BDA5E5EE61E8DC7DD5B174}
        // post非传文件     headerMap=={host=localhost:8080, connection=keep-alive, content-length=169, cache-control=max-age=0, sec-ch-ua=" Not;A Brand";v="99", "Microsoft Edge";v="97", "Chromium";v="97", sec-ch-ua-mobile=?0, sec-ch-ua-platform="Windows", upgrade-insecure-requests=1, origin=http://localhost:8080, content-type=application/x-www-form-urlencoded, user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.69, accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9, sec-fetch-site=same-origin, sec-fetch-mode=navigate, sec-fetch-user=?1, sec-fetch-dest=document, referer=http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/request.html, accept-encoding=gzip, deflate, br, accept-language=zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7, cookie=JSESSIONID=7234876432BDA5E5EE61E8DC7DD5B174}
        // post传文件httpHeaders==[host:"localhost:8080", connection:"keep-alive", content-length:"995", cache-control:"max-age=0", sec-ch-ua:"" Not;A Brand";v="99", "Microsoft Edge";v="97", "Chromium";v="97"", sec-ch-ua-mobile:"?0", sec-ch-ua-platform:""Windows"", upgrade-insecure-requests:"1", origin:"http://localhost:8080", content-type:"multipart/form-data; boundary=----WebKitFormBoundaryJyY7LwvrRBux7Wsr", user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.69", accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", sec-fetch-site:"same-origin", sec-fetch-mode:"navigate", sec-fetch-user:"?1", sec-fetch-dest:"document", referer:"http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/request.html", accept-encoding:"gzip, deflate, br", accept-language:"zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", cookie:"JSESSIONID=7234876432BDA5E5EE61E8DC7DD5B174"]
        log.info("headerMap=={}", headerMap);
        // get时     httpHeaders==[host:"localhost:8080", connection:"keep-alive",                                                  sec-ch-ua:"" Not;A Brand";v="99", "Microsoft Edge";v="97", "Chromium";v="97"", sec-ch-ua-mobile:"?0", sec-ch-ua-platform:""Windows"", upgrade-insecure-requests:"1",                                                                                   user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.69", accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", sec-fetch-site:"same-origin", sec-fetch-mode:"navigate", sec-fetch-user:"?1", sec-fetch-dest:"document", referer:"http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/request.html", accept-encoding:"gzip, deflate, br", accept-language:"zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", cookie:"JSESSIONID=7234876432BDA5E5EE61E8DC7DD5B174"]
        // post时    httpHeaders==[host:"localhost:8080", connection:"keep-alive", content-length:"169", cache-control:"max-age=0", sec-ch-ua:"" Not;A Brand";v="99", "Microsoft Edge";v="97", "Chromium";v="97"", sec-ch-ua-mobile:"?0", sec-ch-ua-platform:""Windows"", upgrade-insecure-requests:"1", origin:"http://localhost:8080", content-type:"application/x-www-form-urlencoded", user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.69", accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", sec-fetch-site:"same-origin", sec-fetch-mode:"navigate", sec-fetch-user:"?1", sec-fetch-dest:"document", referer:"http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/request.html", accept-encoding:"gzip, deflate, br", accept-language:"zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", cookie:"JSESSIONID=7234876432BDA5E5EE61E8DC7DD5B174"]
        log.info("httpHeaders=={}", httpHeaders);       // httpHeaders==[host:"localhost:8080", connection:"keep-alive", cache-control:"max-age=0", sec-ch-ua:"" Not;A Brand";v="99", "Microsoft Edge";v="97", "Chromium";v="97"", sec-ch-ua-mobile:"?0", sec-ch-ua-platform:""Windows"", upgrade-insecure-requests:"1", user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.69", accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", sec-fetch-site:"none", sec-fetch-mode:"navigate", sec-fetch-user:"?1", sec-fetch-dest:"document", accept-encoding:"gzip, deflate, br", accept-language:"zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", cookie:"JSESSIONID=8FA4D93F318910C0E6354C3683ECEADB"]
        log.info("jsessionId=={}", jsessionId);         // jsessionId==DA986137DC080013719DAAC0B37A4A13
        log.info("cookie=={}", bean2Str(cookie));       // cookie=={"name":"JSESSIONID","value":"1214F73476FC239744679B9AAB9FBDC0","version":0,"comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"httpOnly":false}
        log.info("requestBody=={}", requestBody);       // 【post的非传文件才有】  name=%E5%BC%A0%E4%B8%89&hobby=%E5%90%83&hobby=%E5%96%9D&hobby2=%E5%AB%96&hobby2=%E8%B5%8C&hobby3=%E7%8E%A9&hobby3=%E4%B9%90&photo=%E6%96%87%E6%A1%A3%E8%A7%86%E9%A2%91.md
        log.info("httpMethod=={}", httpMethod);         // GET、POST
        log.info("requestAttr=={}", requestAttr);       // requestAttr==null
        log.info("map=={}", map);                       //      map=={user=User(id=null, name=张三, hobby=吃,喝, hobby2=[嫖, 赌], hobby3=[玩, 乐], multipartFile=null), org.springframework.validation.BindingResult.user=org.springframework.validation.BeanPropertyBindingResult: 0 errors, org.springframework.validation.BindingResult.string=org.springframework.validation.BeanPropertyBindingResult: 0 errors, modelAndView=ModelAndView [view=[null]; model=null], org.springframework.validation.BindingResult.modelAndView=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
        log.info("model=={}", model);                   //    model=={user=User(id=null, name=张三, hobby=吃,喝, hobby2=[嫖, 赌], hobby3=[玩, 乐], multipartFile=null), org.springframework.validation.BindingResult.user=org.springframework.validation.BeanPropertyBindingResult: 0 errors, org.springframework.validation.BindingResult.string=org.springframework.validation.BeanPropertyBindingResult: 0 errors, modelAndView=ModelAndView [view=[null]; model=null], org.springframework.validation.BindingResult.modelAndView=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
        log.info("modelMap=={}", modelMap);             // modelMap=={user=User(id=null, name=张三, hobby=吃,喝, hobby2=[嫖, 赌], hobby3=[玩, 乐], multipartFile=null), org.springframework.validation.BindingResult.user=org.springframework.validation.BeanPropertyBindingResult: 0 errors, org.springframework.validation.BindingResult.string=org.springframework.validation.BeanPropertyBindingResult: 0 errors, modelAndView=ModelAndView [view=[null]; model=null], org.springframework.validation.BindingResult.modelAndView=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
        log.info("modelAndView=={}", modelAndView);     // modelAndView==ModelAndView [view=[null]; model=null]

        log.warn("==SpringMVC封装的 请求实体requestEntity====================================");
        log.info("requestEntity=={}", requestEntity);   // requestEntity==<POST http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/all/%E7%A1%95/%E5%90%83,%E5%96%9D,name=%E5%BC%A0%E4%B8%89&hobby=%E5%90%83&hobby=%E5%96%9D&hobby2=%E5%AB%96&hobby2=%E8%B5%8C&hobby3=%E7%8E%A9&hobby3=%E4%B9%90&photo=%E6%96%87%E6%A1%A3%E8%A7%86%E9%A2%91.md,[host:"localhost:8080", connection:"keep-alive", content-length:"169", cache-control:"max-age=0", sec-ch-ua:"" Not;A Brand";v="99", "Microsoft Edge";v="97", "Chromium";v="97"", sec-ch-ua-mobile:"?0", sec-ch-ua-platform:""Windows"", upgrade-insecure-requests:"1", origin:"http://localhost:8080", user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.69", accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", sec-fetch-site:"same-origin", sec-fetch-mode:"navigate", sec-fetch-user:"?1", sec-fetch-dest:"document", referer:"http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/request.html", accept-encoding:"gzip, deflate, br", accept-language:"zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", cookie:"JSESSIONID=7234876432BDA5E5EE61E8DC7DD5B174", Content-Type:"application/x-www-form-urlencoded;charset=UTF-8"]>
        log.info("{}", requestEntity.getMethod());      // POST
        // GET时     http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/all/%E7%A1%95/%E5%90%83,%E5%96%9D?name=%E5%BC%A0%E4%B8%89&hobby=%E5%90%83&hobby=%E5%96%9D&hobby2=%E5%AB%96&hobby2=%E8%B5%8C&hobby3=%E7%8E%A9&hobby3=%E4%B9%90&photo=%E6%96%87%E6%A1%A3%E8%A7%86%E9%A2%91.md
        // POST时    http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/all/%E7%A1%95/%E5%90%83,%E5%96%9D
        log.info("{}", requestEntity.getUrl());
        log.info("{}", requestEntity.getBody());        // 【post的非传文件才有】  name=张三&hobby=吃&hobby=喝&hobby2=嫖&hobby2=赌&hobby3=玩&hobby3=乐&photo=文档视频.md
        log.info("{}", requestEntity.getType());        // 【post的非传文件才有】  class java.lang.String
        // [host:"localhost:8080", connection:"keep-alive",                                                  sec-ch-ua:"" Not;A Brand";v="99", "Microsoft Edge";v="97", "Chromium";v="97"", sec-ch-ua-mobile:"?0", sec-ch-ua-platform:""Windows"", upgrade-insecure-requests:"1",                                 user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.69", accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", sec-fetch-site:"same-origin", sec-fetch-mode:"navigate", sec-fetch-user:"?1", sec-fetch-dest:"document", referer:"http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/request.html", accept-encoding:"gzip, deflate, br", accept-language:"zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", cookie:"JSESSIONID=7234876432BDA5E5EE61E8DC7DD5B174"]
        // [host:"localhost:8080", connection:"keep-alive", content-length:"169", cache-control:"max-age=0", sec-ch-ua:"" Not;A Brand";v="99", "Microsoft Edge";v="97", "Chromium";v="97"", sec-ch-ua-mobile:"?0", sec-ch-ua-platform:""Windows"", upgrade-insecure-requests:"1", origin:"http://localhost:8080", user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.69", accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", sec-fetch-site:"same-origin", sec-fetch-mode:"navigate", sec-fetch-user:"?1", sec-fetch-dest:"document", referer:"http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/request.html", accept-encoding:"gzip, deflate, br", accept-language:"zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", cookie:"JSESSIONID=7234876432BDA5E5EE61E8DC7DD5B174", Content-Type:"application/x-www-form-urlencoded;charset=UTF-8"]
        // [host:"localhost:8080", connection:"keep-alive", content-length:"995", cache-control:"max-age=0", sec-ch-ua:"" Not;A Brand";v="99", "Microsoft Edge";v="97", "Chromium";v="97"", sec-ch-ua-mobile:"?0", sec-ch-ua-platform:"" Windows "", upgrade-insecure-requests:"1", origin:"http://localhost:8080", user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.69", accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", sec-fetch-site:"same-origin", sec-fetch-mode:"navigate", sec-fetch-user:"?1", sec-fetch-dest:"document", referer:"http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/request.html", accept-encoding:"gzip, deflate, br", accept-language:"zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", cookie:"JSESSIONID=7234876432BDA5E5EE61E8DC7DD5B174", Content-Type:"multipart/form-data;boundary=----WebKitFormBoundaryJyY7LwvrRBux7Wsr;charset=UTF-8"]
        log.info("{}", requestEntity.getHeaders());

        log.info("webRequest=={}", webRequest);             // webRequest==ServletWebRequest: uri=/a4/all/%E7%A1%95/%E5%90%83,%E5%96%9D;client=127.0.0.1;session=7234876432BDA5E5EE61E8DC7DD5B174
        log.info("{}", webRequest.getParameterMap());       // org.apache.catalina.util.ParameterMap@307f039d
        log.info("{}", webRequest.getHeaderNames());        // org.springframework.util.CollectionUtils$EnumerationIterator@20693de1
        log.info("{}", webRequest.getUserPrincipal());      // null
        log.info("{}", webRequest.getLocale());             // zh_CN
        log.info("multipartRequest=={}", multipartRequest); // multipartRequest==null
        if (multipartRequest != null) {
            log.info("{}", multipartRequest.getMultiFileMap());
            log.info("{}", multipartRequest.getFileNames());
        }

        log.info("SessionStatus=={}", bean2Str(sessionStatus));     // SessionStatus=={"complete":false}
        log.info("sessionAttr=={}", sessionAttr);                   // sessionAttr==sessionAttr

        // == 分割线 ===========================================================================


        log.warn("==请求转发====================================");
//        request.getRequestDispatcher("/A01Servlet").forward(request,response); //请求转发

        log.warn("==字符集信息====================================");
        log.info("{}", request.getCharacterEncoding());  // UTF-8

        log.warn("==客户端信息====================================");
        log.info("{}", request.getRemoteAddr());     // 192.168.0.106
        log.info("{}", request.getRemoteHost());     // 192.168.0.106
        log.info("{}", request.getRemotePort());     // 44450
        log.info("{}", request.getRemoteUser());     // null

        log.warn("==本地信息====================================");
        log.info("{}", request.getLocalAddr());      // localhost
        log.info("{}", request.getLocalName());      // lishuoboy-PC
        log.info("{}", request.getLocalPort());      // 8080

        log.warn("==请求行信息====================================");
        log.info("{}", request.getMethod());         // GET
        log.info("{}", request.getProtocol());       // HTTP/1.1
        log.info("{}", request.getRequestURL());     // http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/all/%E7%A1%95/%E5%90%83,%E5%96%9D
        log.info("{}", request.getScheme());         // http
        log.info("{}", request.getServerName());     // localhost
        log.info("{}", request.getServerPort());     // 8080
        log.info("{}", request.getRequestURI());     // /a4/all/%E7%A1%95/%E5%90%83,%E5%96%9D
        log.info("{}", request.getContextPath());    // 项目路径，本测试例中为空串。springmvc例子中为 /lishuoboy_springmvc_war_exploded
        log.info("{}", request.getServletPath());    // /a4/all/硕/吃,喝
        log.info("{}", request.getQueryString());    // 【get才有】 name=%E5%BC%A0%E4%B8%89&hobby=%E5%90%83&hobby=%E5%96%9D&hobby2=%E5%AB%96&hobby2=%E8%B5%8C&hobby3=%E7%8E%A9&hobby3=%E4%B9%90&photo=%E6%96%87%E6%A1%A3%E8%A7%86%E9%A2%91.md

        log.warn("==请求头信息====================================");
//        host==localhost:8080, request.getHeaders==[localhost:8080]
//        connection==keep-alive, request.getHeaders==[keep-alive]
//        【post才有】content-length==169, request.getHeaders==[169]
//        【post才有】cache-control==max-age=0, request.getHeaders==[max-age=0]
//        sec-ch-ua==" Not;A Brand";v="99", "Microsoft Edge";v="97", "Chromium";v="97", request.getHeaders==[" Not;A Brand";v="99", "Microsoft Edge";v="97", "Chromium";v="97"]
//        sec-ch-ua-mobile==?0, request.getHeaders==[?0]
//        sec-ch-ua-platform=="Windows", request.getHeaders==["Windows"]
//        upgrade-insecure-requests==1, request.getHeaders==[1]
//        【post才有】origin==http://localhost:8080, request.getHeaders==[http://localhost:8080]
//        【post非传文件才有】content-type==application/x-www-form-urlencoded, request.getHeaders==[application/x-www-form-urlencoded]
//        【post传文件才有  】content-type==multipart/form-data; boundary=----WebKitFormBoundaryJyY7LwvrRBux7Wsr, request.getHeaders==[multipart/form-data; boundary=----WebKitFormBoundaryJyY7LwvrRBux7Wsr]
//        user-agent==Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.69, request.getHeaders==[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.69]
//        accept==text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9, request.getHeaders==[text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9]
//        sec-fetch-site==same-origin, request.getHeaders==[same-origin]
//        sec-fetch-mode==navigate, request.getHeaders==[navigate]
//        sec-fetch-user==?1, request.getHeaders==[?1]
//        sec-fetch-dest==document, request.getHeaders==[document]
//        referer==http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/request.html, request.getHeaders==[http://localhost:8080/lishuoboy_springmvc_war_exploded/a4/request.html]
//        accept-encoding==gzip, deflate, br, request.getHeaders==[gzip, deflate, br]
//        accept-language==zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7, request.getHeaders==[zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7]
//        cookie==JSESSIONID=7234876432BDA5E5EE61E8DC7DD5B174, request.getHeaders==[JSESSIONID=7234876432BDA5E5EE61E8DC7DD5B174]
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            log.info("{}=={}, request.getHeaders=={}", headerName, request.getHeader(headerName), ListUtil.list(false, request.getHeaders(headerName)));
        }

        log.warn("==请求体信息====================================");
        // get             null
        // post非传文件     application/x-www-form-urlencoded
        // post  传文件     multipart/form-data; boundary=----WebKitFormBoundaryJyY7LwvrRBux7Wsr
        log.info("{}", request.getContentType());        // post才有
        // get             -1
        // post非传文件     169
        // post  传文件     995
        log.info("{}", request.getContentLength());
        log.info("{}", request.getContentLengthLong());
//        log.info("{}", IoUtil.read(request.getReader(),false));
        // 对于post请求，获取流与获取参数只能获取一次，后获取的就没数据了，先注释掉
//        log.info("{}", IoUtil.read(request.getInputStream(), StandardCharsets.UTF_8));  // post才有  name=硕&hobby=吃&hobby=喝&hobby2=嫖&hobby2=赌

        log.warn("==请求参数信息1====================================");
        // 对于post请求，获取流与获取参数只能获取一次，后获取的就没数据了
        // name==张三, name==[张三]
        // hobby==吃, hobby==[吃, 喝]
        // hobby2==嫖, hobby2==[嫖, 赌]
        // hobby3==玩, hobby3==[玩, 乐]
        // 【post传文件没有】photo==文档视频.md, photo==[文档视频.md]
        Enumeration<String> paramNames = request.getParameterNames();
        while (paramNames.hasMoreElements()) {
            String paramName = paramNames.nextElement();
            log.info("{}=={}, {}=={}", paramName, request.getParameter(paramName), paramName, request.getParameterValues(paramName));
        }

        log.warn("==请求参数信息2====================================");
        // 对于post请求，获取流与获取参数只能获取一次，后获取的就没数据了
        // name==[张三]
        // hobby==[吃, 喝]
        // hobby2==[嫖, 赌]
        // hobby3==[玩, 乐]
        // 【post传文件没有】photo==[文档视频.md]
        Map<String, String[]> paramMap2 = request.getParameterMap();
        for (Map.Entry<String, String[]> strEntry : paramMap2.entrySet()) {
            log.info("{}=={}", strEntry.getKey(), strEntry.getValue());
        }

        log.warn("==cookie 信息====================================");
        Cookie[] cookies = request.getCookies();
        for (int i = 0; cookies != null && i < cookies.length; i++) {
            log.info("cookies[{}]=={}", i, BeanUtil.beanToMap(cookies[i]));  // cookies[0]=={name=JSESSIONID, value=75337EA92CF966565E6E6B822F8F6C02, version=0, comment=null, domain=null, maxAge=-1, path=null, secure=false, httpOnly=false}
        }

        log.warn("==request属性（四个作用域之一）信息====================================");
        // attributeName==org.springframework.web.servlet.View.pathVariables, request.getAttribute=={pName=硕, pHobby=[吃, 喝]}
        // attributeName==org.springframework.web.context.request.async.WebAsyncManager.WEB_ASYNC_MANAGER, request.getAttribute==org.springframework.web.context.request.async.WebAsyncManager@344ad179
        // attributeName==org.springframework.web.servlet.HandlerMapping.bestMatchingHandler, request.getAttribute==top.lishuoboy.boot03web.a4.ParamController#testAll(String, String, String[], List, Map, String, String, String[], List, Map, User, MultipartFile, String, Map, HttpHeaders, String, Cookie, Object, String, HttpMethod, Map, Model, ModelAndView, RequestEntity, WebRequest, MultipartRequest, Object, SessionStatus, HttpServletRequest, HttpServletResponse, HttpSession, Locale, Principal, TimeZone, ZoneId, PushBuilder, UriComponentsBuilder, BindingResult, Errors, String)
        // attributeName==hiddenHttpMethodFilter.FILTERED, request.getAttribute==true
        // attributeName==org.springframework.web.servlet.DispatcherServlet.CONTEXT, request.getAttribute==org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@b6b1987, started on Mon Jan 24 23:48:04 CST 2022
        // attributeName==org.springframework.web.servlet.resource.ResourceUrlProvider, request.getAttribute==org.springframework.web.servlet.resource.ResourceUrlProvider@296e281a
        // attributeName==characterEncodingFilter.FILTERED, request.getAttribute==true
        // attributeName==org.springframework.web.servlet.HandlerMapping.lookupPath, request.getAttribute==/a4/all/硕/吃,喝
        // attributeName==org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE, request.getAttribute==org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@b6b1987, started on Mon Jan 24 23:48:04 CST 2022
        // attributeName==org.springframework.web.servlet.DispatcherServlet.LOCALE_RESOLVER, request.getAttribute==org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@44b8dde4
        // attributeName==formContentFilter.FILTERED, request.getAttribute==true
        // attributeName==org.springframework.web.servlet.HandlerMapping.bestMatchingPattern, request.getAttribute==/a4/all/{pName}/{pHobby}
        // attributeName==requestContextFilter.FILTERED, request.getAttribute==true
        // attributeName==org.springframework.web.servlet.DispatcherServlet.OUTPUT_FLASH_MAP, request.getAttribute==FlashMap [attributes={}, targetRequestPath=null, targetRequestParams={}]
        // attributeName==org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping, request.getAttribute==/a4/all/硕/吃,喝
        // attributeName==org.springframework.web.servlet.DispatcherServlet.FLASH_MAP_MANAGER, request.getAttribute==org.springframework.web.servlet.support.SessionFlashMapManager@741386a5
        // attributeName==org.springframework.web.servlet.HandlerMapping.uriTemplateVariables, request.getAttribute=={pName=硕, pHobby=吃,喝}
        // attributeName==org.springframework.web.servlet.DispatcherServlet.THEME_RESOLVER, request.getAttribute==org.springframework.web.servlet.theme.FixedThemeResolver@6cd26df
        // attributeName==org.springframework.core.convert.ConversionService, request.getAttribute==ConversionService converters =	@org.springframework.format.annotation.DateTimeFormat java.lang.Long -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@1f0b95cf,@org.springframework.format.annotation.NumberFormat java.lang.Long -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4c61e351	@org.springframework.format.annotation.DateTimeFormat java.time.LocalDate -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@6144632e,java.time.LocalDate -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@11a577df	@org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@6144632e,java.time.LocalDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@543bddbb	@org.springframework.format.annotation.DateTimeFormat java.time.LocalTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@6144632e,java.time.LocalTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@779350ef	@org.springframework.format.annotation.DateTimeFormat java.time.OffsetDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@6144632e,java.time.OffsetDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@56b9903b	@org.springframework.format.annotation.DateTimeFormat java.time.OffsetTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@6144632e,java.time.OffsetTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@72b1fb29	@org.springframework.format.annotation.DateTimeFormat java.time.ZonedDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@6144632e,java.time.ZonedDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@722d6155	@org.springframework.format.annotation.DateTimeFormat java.util.Calendar -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@1f0b95cf	@org.springframework.format.annotation.DateTimeFormat java.util.Date -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@1f0b95cf	@org.springframework.format.annotation.NumberFormat java.lang.Byte -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4c61e351	@org.springframework.format.annotation.NumberFormat java.lang.Double -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4c61e351	@org.springframework.format.annotation.NumberFormat java.lang.Float -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4c61e351	@org.springframework.format.annotation.NumberFormat java.lang.Integer -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4c61e351	@org.springframework.format.annotation.NumberFormat java.lang.Short -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4c61e351	@org.springframework.format.annotation.NumberFormat java.math.BigDecimal -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4c61e351	@org.springframework.format.annotation.NumberFormat java.math.BigInteger -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4c61e351	java.lang.Boolean -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@5a3d165a	java.lang.Character -> java.lang.Number : org.springframework.core.convert.support.CharacterToNumberFactory@5cb68f3b	java.lang.Character -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@4e93b312	java.lang.Enum -> java.lang.Integer : org.springframework.core.convert.support.EnumToIntegerConverter@2a1b1826	java.lang.Enum -> java.lang.String : org.springframework.core.convert.support.EnumToStringConverter@d413241	java.lang.Integer -> java.lang.Enum : org.springframework.core.convert.support.IntegerToEnumConverterFactory@392d59f6	java.lang.Long -> java.time.Instant : org.springframework.format.datetime.standard.DateTimeConverters$LongToInstantConverter@49125af3	java.lang.Long -> java.util.Calendar : org.springframework.format.datetime.DateFormatterRegistrar$LongToCalendarConverter@19c13242,java.lang.Long -> java.util.Calendar : org.springframework.format.datetime.DateFormatterRegistrar$LongToCalendarConverter@5881b560	java.lang.Long -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$LongToDateConverter@7795df4e,java.lang.Long -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$LongToDateConverter@135dd741	java.lang.Number -> java.lang.Character : org.springframework.core.convert.support.NumberToCharacterConverter@1fb0359e	java.lang.Number -> java.lang.Number : org.springframework.core.convert.support.NumberToNumberConverterFactory@70d5c3f	java.lang.Number -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@1a8a08f9	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.lang.Long: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@1f0b95cf,java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Long: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4c61e351	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.LocalDate: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@6144632e,java.lang.String -> java.time.LocalDate: org.springframework.format.datetime.standard.TemporalAccessorParser@518412af	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@6144632e,java.lang.String -> java.time.LocalDateTime: org.springframework.format.datetime.standard.TemporalAccessorParser@57109a8a	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.LocalTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@6144632e,java.lang.String -> java.time.LocalTime: org.springframework.format.datetime.standard.TemporalAccessorParser@98af70d	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.OffsetDateTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@6144632e,java.lang.String -> java.time.OffsetDateTime: org.springframework.format.datetime.standard.TemporalAccessorParser@703e8006	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.OffsetTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@6144632e,java.lang.String -> java.time.OffsetTime: org.springframework.format.datetime.standard.TemporalAccessorParser@2892ea2c	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.ZonedDateTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@6144632e,java.lang.String -> java.time.ZonedDateTime: org.springframework.format.datetime.standard.TemporalAccessorParser@39268abf	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.util.Calendar: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@1f0b95cf	java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.util.Date: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@1f0b95cf	java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Byte: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4c61e351	java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Double: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4c61e351	java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Float: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4c61e351	java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Integer: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4c61e351	java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Short: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4c61e351	java.lang.String -> @org.springframework.format.annotation.NumberFormat java.math.BigDecimal: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4c61e351	java.lang.String -> @org.springframework.format.annotation.NumberFormat java.math.BigInteger: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@4c61e351	java.lang.String -> java.lang.Boolean : org.springframework.core.convert.support.StringToBooleanConverter@33c3eb15	java.lang.String -> java.lang.Character : org.springframework.core.convert.support.StringToCharacterConverter@73585307	java.lang.String -> java.lang.Enum : org.springframework.core.convert.support.StringToEnumConverterFactory@68191cb6	java.lang.String -> java.lang.Number : org.springframework.core.convert.support.StringToNumberConverterFactory@191294ee	java.lang.String -> java.nio.charset.Charset : org.springframework.core.convert.support.StringToCharsetConverter@5bd9674f	java.lang.String -> java.time.Duration: org.springframework.format.datetime.standard.DurationFormatter@382a7229	java.lang.String -> java.time.Instant: org.springframework.format.datetime.standard.InstantFormatter@43d7cf4	java.lang.String -> java.time.Month: org.springframework.format.datetime.standard.MonthFormatter@425604cb	java.lang.String -> java.time.MonthDay: org.springframework.format.datetime.standard.MonthDayFormatter@4ca40335	java.lang.String -> java.time.Period: org.springframework.format.datetime.standard.PeriodFormatter@648e9af1	java.lang.String -> java.time.Year: org.springframework.format.datetime.standard.YearFormatter@427d57a1	java.lang.String -> java.time.YearMonth: org.springframework.format.datetime.standard.YearMonthFormatter@2a1ccc67	java.lang.String -> java.util.Currency : org.springframework.core.convert.support.StringToCurrencyConverter@12aeb72f	java.lang.String -> java.util.Locale : org.springframework.core.convert.support.StringToLocaleConverter@4857ef64	java.lang.String -> java.util.Properties : org.springframework.core.convert.support.StringToPropertiesConverter@4711e76a	java.lang.String -> java.util.TimeZone : org.springframework.core.convert.support.StringToTimeZoneConverter@7f6201cd	java.lang.String -> java.util.UUID : org.springframework.core.convert.support.StringToUUIDConverter@6b5c47ca	java.nio.charset.Charset -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@49575692	java.time.Duration -> java.lang.String : org.springframework.format.datetime.standard.DurationFormatter@382a7229	java.time.Instant -> java.lang.Long : org.springframework.format.datetime.standard.DateTimeConverters$InstantToLongConverter@173be22a	java.time.Instant -> java.lang.String : org.springframework.format.datetime.standard.InstantFormatter@43d7cf4	java.time.LocalDateTime -> java.time.LocalDate : org.springframework.format.datetime.standard.DateTimeConverters$LocalDateTimeToLocalDateConverter@12bc00c2	java.time.LocalDateTime -> java.time.LocalTime : org.springframework.format.datetime.standard.DateTimeConverters$LocalDateTimeToLocalTimeConverter@6936c76a	java.time.Month -> java.lang.String : org.springframework.format.datetime.standard.MonthFormatter@425604cb	java.time.MonthDay -> java.lang.String : org.springframework.format.datetime.standard.MonthDayFormatter@4ca40335	java.time.OffsetDateTime -> java.time.Instant : org.springframework.format.datetime.standard.DateTimeConverters$OffsetDateTimeToInstantConverter@6311d555	java.time.OffsetDateTime -> java.time.LocalDate : org.springframework.format.datetime.standard.DateTimeConverters$OffsetDateTimeToLocalDateConverter@60b81291	java.time.OffsetDateTime -> java.time.LocalDateTime : org.springframework.format.datetime.standard.DateTimeConverters$OffsetDateTimeToLocalDateTimeConverter@3ba45c45	java.time.OffsetDateTime -> java.time.LocalTime : org.springframework.format.datetime.standard.DateTimeConverters$OffsetDateTimeToLocalTimeConverter@521f5762	java.time.OffsetDateTime -> java.time.ZonedDateTime : org.springframework.format.datetime.standard.DateTimeConverters$OffsetDateTimeToZonedDateTimeConverter@74ad1dca	java.time.Period -> java.lang.String : org.springframework.format.datetime.standard.PeriodFormatter@648e9af1	java.time.Year -> java.lang.String : org.springframework.format.datetime.standard.YearFormatter@427d57a1	java.time.YearMonth -> java.lang.String : org.springframework.format.datetime.standard.YearMonthFormatter@2a1ccc67	java.time.ZoneId -> java.util.TimeZone : org.springframework.core.convert.support.ZoneIdToTimeZoneConverter@3a35b032	java.time.ZonedDateTime -> java.time.Instant : org.springframework.format.datetime.standard.DateTimeConverters$ZonedDateTimeToInstantConverter@4635ad39	java.time.ZonedDateTime -> java.time.LocalDate : org.springframework.format.datetime.standard.DateTimeConverters$ZonedDateTimeToLocalDateConverter@26d4dec0	java.time.ZonedDateTime -> java.time.LocalDateTime : org.springframework.format.datetime.standard.DateTimeConverters$ZonedDateTimeToLocalDateTimeConverter@37199252	java.time.ZonedDateTime -> java.time.LocalTime : org.springframework.format.datetime.standard.DateTimeConverters$ZonedDateTimeToLocalTimeConverter@4469e60	java.time.ZonedDateTime -> java.time.OffsetDateTime : org.springframework.format.datetime.standard.DateTimeConverters$ZonedDateTimeToOffsetDateTimeConverter@26b43fa8	java.time.ZonedDateTime -> java.util.Calendar : org.springframework.core.convert.support.ZonedDateTimeToCalendarConverter@33a5e3f9	java.util.Calendar -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToLongConverter@6b2651ae,java.util.Calendar -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToLongConverter@453369d0	java.util.Calendar -> java.time.Instant : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToInstantConverter@b3d7a8	java.util.Calendar -> java.time.LocalDate : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToLocalDateConverter@5b4ee284	java.util.Calendar -> java.time.LocalDateTime : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToLocalDateTimeConverter@18f276c4	java.util.Calendar -> java.time.LocalTime : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToLocalTimeConverter@e69fd1f	java.util.Calendar -> java.time.OffsetDateTime : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToOffsetDateTimeConverter@37e14986	java.util.Calendar -> java.time.ZonedDateTime : org.springframework.format.datetime.standard.DateTimeConverters$CalendarToZonedDateTimeConverter@73669fd8	java.util.Calendar -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToDateConverter@16f46257,java.util.Calendar -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToDateConverter@3e9cff77	java.util.Currency -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@726f6e7a	java.util.Date -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$DateToLongConverter@2e655ad9,java.util.Date -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$DateToLongConverter@2ab2a66e	java.util.Date -> java.util.Calendar : org.springframework.format.datetime.DateFormatterRegistrar$DateToCalendarConverter@5a280881,java.util.Date -> java.util.Calendar : org.springframework.format.datetime.DateFormatterRegistrar$DateToCalendarConverter@68154a58	java.util.Locale -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@5875649e	java.util.Properties -> java.lang.String : org.springframework.core.convert.support.PropertiesToStringConverter@2208055f	java.util.UUID -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@3ff1cf89	org.springframework.core.convert.support.ArrayToArrayConverter@3a564fa9	org.springframework.core.convert.support.ArrayToCollectionConverter@bc819d6	org.springframework.core.convert.support.ArrayToObjectConverter@27bdc7ec	org.springframework.core.convert.support.ArrayToStringConverter@7a0e52ba	org.springframework.core.convert.support.ByteBufferConverter@734bae14	org.springframework.core.convert.support.ByteBufferConverter@734bae14	org.springframework.core.convert.support.ByteBufferConverter@734bae14	org.springframework.core.convert.support.ByteBufferConverter@734bae14	org.springframework.core.convert.support.CollectionToArrayConverter@27991236	org.springframework.core.convert.support.CollectionToCollectionConverter@36fce7b2	org.springframework.core.convert.support.CollectionToObjectConverter@28300320	org.springframework.core.convert.support.CollectionToStringConverter@30ba92d7	org.springframework.core.convert.support.FallbackObjectToStringConverter@5cd67626	org.springframework.core.convert.support.IdToEntityConverter@1b0aa8de,org.springframework.core.convert.support.ObjectToObjectConverter@269a7f40	org.springframework.core.convert.support.MapToMapConverter@7f09993b	org.springframework.core.convert.support.ObjectToArrayConverter@6c8798f3	org.springframework.core.convert.support.ObjectToCollectionConverter@dcdd7dc	org.springframework.core.convert.support.ObjectToOptionalConverter@72c4ccb	org.springframework.core.convert.support.ObjectToOptionalConverter@72c4ccb	org.springframework.core.convert.support.ObjectToOptionalConverter@72c4ccb	org.springframework.core.convert.support.StreamConverter@661ed33d	org.springframework.core.convert.support.StreamConverter@661ed33d	org.springframework.core.convert.support.StreamConverter@661ed33d	org.springframework.core.convert.support.StreamConverter@661ed33d	org.springframework.core.convert.support.StringToArrayConverter@489d457a	org.springframework.core.convert.support.StringToCollectionConverter@6308d315
        Enumeration<String> attributeNames = request.getAttributeNames();
        while (attributeNames.hasMoreElements()) {
            String attributeName = attributeNames.nextElement();
            log.info("attributeName=={}, request.getAttribute=={}", attributeName, StrUtil.removeAll(String.valueOf(request.getAttribute(attributeName)), StrPool.LF));
        }

        log.warn("==地区信息====================================");
        log.info("{}", request.getLocale());    // zh_CN
        Enumeration<Locale> locales = request.getLocales();
        log.info("{}", ListUtil.list(false, locales));  // [zh_CN, zh, en, en_GB, en_US]

        log.warn("==用户主体信息====================================");
        log.info("{}", request.getUserPrincipal());             //  null

        log.warn("==其它信息====================================");
        log.info("{}", request.getAuthType());                  // null
        log.info("{}", request.getPathInfo());                  // null
        log.info("{}", request.getPathTranslated());            // null
        log.info("{}", request.getTrailerFields());             // {}
//        log.info("{}", request.getAsyncContext());
        log.info("{}", BeanUtil.beanToMap(request.getHttpServletMapping())); // springMVC是 {matchValue=, pattern=/, servletName=springMVC}。  原生servlet是 {matchValue=A02Servlet, pattern=/A02Servlet, servletName=A02Servlet}

        // session
        log.warn("==session 信息====================================");
        log.warn("{}", session == request.getSession());        // true
        log.info("{}", session.isNew());                        // true/false
        log.warn("{}", session.getId());                        // 982532BDEC32B4D69EDED315405600AB
        log.info("{}", session.getCreationTime());              // 1641378245676
        log.info("{}", session.getLastAccessedTime());          // 1641378245676
        log.info("{}", session.getMaxInactiveInterval());       // 1800

        log.warn("==session 属性（四个作用域之一）信息====================================");
        Enumeration<String> sessionAttributeNames = session.getAttributeNames();
        while (sessionAttributeNames.hasMoreElements()) {
            String sAttributeName = sessionAttributeNames.nextElement();
            log.info("{}=={}", sAttributeName, session.getAttribute(sAttributeName));  // sessionAttr==sessionAttr
        }

        log.warn("==ServletContext 信息====================================");
        ServletContext context = request.getServletContext();
        log.info("{}", context == session.getServletContext());      // true
        // 相对 war 包根路径,在springboot中无效，直接用java原生方式，或用 util
        log.info("{}", context.getResource(""));                                    // file:/D:/workspace/lishuoboy-springmvc/target/lishuoboy-springmvc-1.0-SNAPSHOT/
        log.info("{}", context.getResource("/WEB-INF/classes/logback-test.xml"));   // file:/D:/workspace/lishuoboy-springmvc/target/lishuoboy-springmvc-1.0-SNAPSHOT/WEB-INF/classes/logback-test.xml
        log.info("{}", context.getResource("WEB-INF/classes/logback-test.xml"));    // file:/D:/workspace/lishuoboy-springmvc/target/lishuoboy-springmvc-1.0-SNAPSHOT/WEB-INF/classes/logback-test.xml
        log.info("{}", context.getRealPath(""));                                        // D:\workspace\lishuoboy-springmvc\target\lishuoboy-springmvc-1.0-SNAPSHOT\
        log.info("{}", context.getRealPath("/WEB-INF/classes/logback-test.xml"));       // D:\workspace\lishuoboy-springmvc\target\lishuoboy-springmvc-1.0-SNAPSHOT\WEB-INF\classes\logback-test.xml
        log.info("{}", context.getRealPath("WEB-INF/classes/logback-test.xml\""));      // null
        log.info("{}", context.getContextPath());                // /lishuoboy_springmvc_war_exploded
        log.info("{}", context.getContext(""));                  // null
        log.info("{}", context.getMimeType("aaa.jpg"));      // image/jpeg
        log.info("{}", context.getSessionTimeout());             // 30
        log.info("{}", context.getServerInfo());                 // Apache Tomcat/9.0.46
        log.info("{}", context.getVirtualServerName());          // Tomcat/localhost
        log.info("{}", context.getServletContextName());         // null
        log.info("{}", context.getResponseCharacterEncoding());  // null
        log.info("{}", context.getServletRegistrations());       // {default=org.apache.catalina.core.ApplicationServletRegistration@78a89def, dispatcherServlet=org.apache.catalina.core.ApplicationServletRegistration@42e8c3}
        log.info("{}", context.getDefaultSessionTrackingModes());// [COOKIE, URL]

        log.warn("==context 属性（四个作用域之一）信息====================================");
        // javax.servlet.context.tempdir==C:\Users\lishuoboy\AppData\Local\Temp\tomcat.8080.3592139269194522656\work\Tomcat\localhost\ROOT
        // org.apache.catalina.resources==org.apache.catalina.webresources.StandardRoot@7dae7f2e
        // org.apache.catalina.webappVersion==org.springframework.web.context.WebApplicationContext.ROOT==org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@b6b1987, started on Mon Jan 24 23:48:04 CST 2022
        // org.springframework.web.context.support.ServletContextScope==org.springframework.web.context.support.ServletContextScope@6375d535
        // org.apache.tomcat.InstanceManager==org.apache.catalina.core.DefaultInstanceManager@37680908
        // contextAttr==contextAttr
        // org.apache.catalina.jsp_classpath==/C:/Program Files/Java/jdk1.8.0_311/jre/lib/charsets.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/deploy.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/ext/access-bridge-64. jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/ext/cldrdata.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/ext/dnsns.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/ext/jaccess.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/ext/jfxrt.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/ext/localedata.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/ext/nashorn.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/ext/sunec.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/ext/sunjce_provider.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/ext/sunmscapi.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/ext/sunpkcs11.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/ext/zipfs.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/javaws.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/jce.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/jfr.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/jfxswt.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/jsse.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/management- agent.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/plugin.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/resources.jar;/C:/Program Files/Java/jdk1.8.0_311/jre/lib/rt.jar;/D:/workspace/lishuoboy-springboot/boot03-web/target/classes/;/C:/Users/lishuoboy/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.3.12.RELEASE/spring-boot-starter-web-2.3.12.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/org/springframework/boot/spring-boot-starter/2.3.12.RELEASE/spring-boot-starter-2.3.12.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.3.12.RELEASE/spring-boot-starter-logging-2.3.12.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.13.3/log4j-to-slf4j-2.13.3.jar;/C:/Users/lishuoboy/.m2/repository/org/apache/logging/log4j/log4j-api/2.13.3/log4j-api-2.13.3.jar;/C:/Users/lishuoboy/.m2/repository/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar;/C:/Users/lishuoboy/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar;/C:/Users/lishuoboy/.m2/repository/org/yaml/snakeyaml/1.26/snakeyaml-1.26.jar;/C:/Users/lishuoboy/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.3.12.RELEASE/spring-boot-starter-json-2.3.12.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.11.4/jackson-datatype-jdk8-2.11.4.jar;/C:/Users/lishuoboy/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.11.4/jackson-datatype-jsr310-2.11.4.jar;/C:/Users/lishuoboy/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.11.4/jackson-module-parameter-names-2.11.4.jar;/C:/Users/lishuoboy/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.3.12.RELEASE/spring-boot-starter-tomcat-2.3.12.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.46/tomcat-embed-core-9.0.46.jar;/C:/Users/lishuoboy/.m2/repository/org/glassfish/jakarta.el/3.0.3/jakarta.el-3.0.3.jar;/C:/Users/lishuoboy/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.46/tomcat-embed-websocket-9.0.46.jar;/C:/Users/lishuoboy/.m2/repository/org/springframework/spring-web/5.2.15.RELEASE/spring-web-5.2.15.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/org/springframework/spring-beans/5.2.15.RELEASE/spring-beans-5.2.15.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/org/springframework/spring-webmvc/5.2.15.RELEASE/spring-webmvc-5.2.15.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/org/springframework/spring-aop/5.2.15.RELEASE/spring-aop-5.2.15.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/org/springframework/spring-context/5.2.15.RELEASE/spring-context-5.2.15.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/org/springframework/spring-expression/5.2.15.RELEASE/spring-expression-5.2.15.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar;/C:/Users/lishuoboy/.m2/repository/org/hamcrest/hamcrest/2.2/hamcrest-2.2.jar;/C:/Users/lishuoboy/.m2/repository/org/springframework/spring-core/5.2.15.RELEASE/spring-core-5.2.15.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/org/springframework/spring-jcl/5.2.15.RELEASE/spring-jcl-5.2.15.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/top/lishuoboy/lishuoboy-dependency-base/1.0-SNAPSHOT/lishuoboy-dependency-base-1.0-SNAPSHOT.jar;/C:/Users/lishuoboy/.m2/repository/org/projectlombok/lombok/1.18.20/lombok-1.18.20.jar;/C:/Users/lishuoboy/.m2/repository/cn/hutool/hutool-all/5.7.15/hutool-all-5.7.15.jar;/C:/Users/lishuoboy/.m2/repository/org/apache/commons/commons-lang3/3.10/commons-lang3-3.10.jar;/C:/Users/lishuoboy/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar;/C:/Users/lishuoboy/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar;/C:/Users/lishuoboy/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.11.4/jackson-databind-2.11.4.jar;/C:/Users/lishuoboy/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.11.4/jackson-annotations-2.11.4.jar;/C:/Users/lishuoboy/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.11.4/jackson-core-2.11.4.jar;/C:/Users/lishuoboy/.m2/repository/junit/junit/4.13.2/junit-4.13.2.jar;/C:/Users/lishuoboy/.m2/repository/org/hamcrest/hamcrest-core/2.2/hamcrest-core-2.2.jar;/C:/Users/lishuoboy/.m2/repository/org/springframework/boot/spring-boot-devtools/2.3.12.RELEASE/spring-boot-devtools-2.3.12.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/org/springframework/boot/spring-boot/2.3.12.RELEASE/spring-boot-2.3.12.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.3.12.RELEASE/spring-boot-autoconfigure-2.3.12.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/org/springframework/boot/spring-boot-configuration-processor/2.3.12.RELEASE/spring-boot-configuration-processor-2.3.12.RELEASE.jar;/C:/Users/lishuoboy/.m2/repository/org/webjars/jquery/3.6.0/jquery-3.6.0.jar;/C:/Program Files/JetBrains/IntelliJ IDEA 2021.3/lib/idea_rt.jar
        // javax.websocket.server.ServerContainer==org.apache.tomcat.websocket.server.WsServerContainer@11650d75
        // org.apache.tomcat.JarScanner==org.apache.tomcat.util.scan.StandardJarScanner@1f3c4778
        // org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcherServlet==org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@b6b1987, started on Mon Jan 24 23:48:04 CST 2022
        Enumeration<String> ctxAttributeNames = context.getAttributeNames();
        while (ctxAttributeNames.hasMoreElements()) {
            String ctxAttributeName = ctxAttributeNames.nextElement();
            log.info("{}=={}", ctxAttributeName, context.getAttribute(ctxAttributeName));
        }

        log.warn("==context 初始参数信息====================================");
        Enumeration<String> ctxInitParameterNames = context.getInitParameterNames();
        while (ctxInitParameterNames.hasMoreElements()) {
            String initParameterName = ctxInitParameterNames.nextElement();
            log.info("{}=={}", initParameterName, context.getInitParameter(initParameterName));
        }

//        log.warn("==servlet 初始参数信息====================================");
//        Enumeration<String> reqInitParameterNames = getInitParameterNames();
//        while (reqInitParameterNames.hasMoreElements()) {
//            String initParameterName = reqInitParameterNames.nextElement();
//            log.info("{}=={}", initParameterName, getInitParameter(initParameterName));
//        }

        request.setAttribute("requestAttr", "requestAttr");
        session.setAttribute("sessionAttr", "sessionAttr");
        context.setAttribute("contextAttr", "contextAttr");

        log.info("{}", locale == request.getLocale());
        log.info("locale=={}", locale);             // locale==zh_CN
        log.info("{}", principal == request.getUserPrincipal());    // true
        log.info("principal=={}", principal);       // principal==null
        log.info("timeZone=={}", timeZone);         // timeZone==sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=31,lastRule=null]
        log.info("zoneId=={}", zoneId);             // zoneId==Asia/Shanghai


        log.warn("==pushBuilder====================================");
        log.info("{}", pushBuilder == request.newPushBuilder());        // true
        if (pushBuilder != null) {
            pushBuilder
                .method("get")
                .addHeader("set-cookie", "push测试")
                .queryString("a=aaa&b=bbb")
                .push();
        }

        log.info("uriComponentsBuilder=={}", uriComponentsBuilder.toUriString()); // uriComponentsBuilder==http://localhost:8080/lishuoboy_springmvc_war_exploded
        log.info("uriComponentsBuilder=={}", uriComponentsBuilder.scheme("http").userInfo("userInfo").host("localhost").port(8080).path("path").queryParam("name", "张三")
            .query("query").build(false).toUriString());                // uriComponentsBuilder==http://userInfo@localhost:8080/lishuoboy_springmvc_war_explodedpath?name=张三&query
        log.info("bindingResult=={}", BeanUtil.beanToMap(bindingResult));   // bindingResult=={target=ModelAndView [view=[null]; model=null], objectName=modelAndView, messageCodesResolver=org.springframework.validation.DefaultMessageCodesResolver@81be608, suppressedFields=[Ljava.lang.String;@d847cf6, nestedPath=}
        log.info("errors=={}", BeanUtil.beanToMap(errors));                        // errors=={target=ModelAndView [view=[null]; model=null], objectName=modelAndView, messageCodesResolver=org.springframework.validation.DefaultMessageCodesResolver@23d60b7d, suppressedFields=[Ljava.lang.String;@5acb6852, nestedPath=}

        log.warn("==response====================================");
        log.info("response=={}", response);     // response==org.apache.catalina.connector.ResponseFacade@6137bada
//        response.getWriter().write("aaaa");   // 当前方法返回值不为void时才能生效
        return "测试乱码";
    }

    @RequestMapping(value = "/11")
    @ResponseBody
    private String str() {
        return "测试乱码11";
    }

    @RequestMapping(value = "/12")
    @ResponseBody
    private Map json() {
        return MapUtil.builder("name", "张三").build();
    }
}
